package com.example.private_auto_install.ftpGIt;

import org.apache.log4j.Logger;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description: git 操作
 * @Author: by
 * @Date: 2022-02-23
 **/
public class JGitHandle {
    public static Logger publishLogProcess = Logger.getLogger("publishLogProcess");
    public static Logger publishLog = Logger.getLogger("publishLog");

    //定义本地git路径
    public static String LOCALPATH = "F:\\1111\\testFile";
    //.git文件路径
    public static String LOCALGITFILE = LOCALPATH + File.separator + ".git";
    //远程仓库地址
    public static String REMOTEREPOURI = "http://192.168.20.122:82/zl-simulation-exper/organicchemistry.git";//操作git的用户名
    public static final String USER = "bai1052076271@sina.com";
    //密码
    public static final String PASSWORD = "baiyu1990";
    //git远程仓库服务器ip
    public static final String HOST = "192.168.20.122";

    public static String updateSavePath = LOCALPATH + File.separator + "gitUpdate";

    public static Git rootGit;

    public static CredentialsProvider user;

    public static Repository repository;

    /**
     * 初始化属性
     */
    public static void initConfig(String projectPath,String gitPathHttp) {
        if (projectPath != null && projectPath.length() > 0) {
            //  生物不用取上级目录    然后获取它的上级目录
//            projectPath = projectPath.substring(0, projectPath.lastIndexOf(File.separator));
            //初始化git的基础目录，修改为相对路径
            JGitHandle.LOCALPATH = projectPath;
            updateDatas();
            JGitHandle.REMOTEREPOURI = gitPathHttp;
        } else {
            //获取jar包的相对路径
            projectPath = UtilFile.getProjectPath();
            if (projectPath == null) {
                publishLogProcess.info("获取根目录失败");
                return;
            }
            // 然后获取它的上级目录
            projectPath = projectPath.substring(0, projectPath.lastIndexOf(File.separator));
            // 如果是理化生的话，需要再次取上级目录
            projectPath = projectPath.substring(0, projectPath.lastIndexOf(File.separator));
            //初始化git的基础目录，修改为相对路径
            JGitHandle.LOCALPATH = projectPath;
        }
    }

    /** 修改LOCALPATH 后，需要调用此方法 */
    public static void updateDatas(){
        LOCALGITFILE = LOCALPATH + File.separator + ".git";
        updateSavePath = LOCALPATH + File.separator + "gitUpdate";
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        boolean flag = JGitHandle.gitPull();
        if (flag) {
        }
        Ftp.uploadingFiles();
    }

    /**
     * @return 是否拉取到代码
     */
    public static boolean gitPull() {
        //初始化数据库
        String result = setupRepo();
        String lastId = getLog();
        publishLogProcess.info("本地最后一次拉取的id为------" + lastId);
        // 拉取本地代码
        boolean flag = pullBranchToLocal();
        String uptoDate = getLog();
        publishLogProcess.info("拉取后的id为---------------" + uptoDate);
        if (lastId.equals(uptoDate)) {
            publishLogProcess.info("没有拉取到新的代码");
            return false;
        } else {
            publishLog.info("本次发布的id为---------------" + uptoDate);
            diffToChangeFiles(lastId);
            return true;
        }
    }

    /**
     * 建立与远程仓库的联系，仅需要执行一次   来clone远程仓库
     *
     * @return
     */
    public static String setupRepo() {
        String msg = "";
        try {
            JGitHandle.user = new UsernamePasswordCredentialsProvider(USER, PASSWORD);
            Git git = Git.cloneRepository()
                    .setURI(REMOTEREPOURI)
                    .setCredentialsProvider(JGitHandle.user)
                    .setBranch("master")
                    .setDirectory(new File(LOCALPATH)).call();
            JGitHandle.rootGit = git;
            repository = git.getRepository();

            msg = "git init success！";
        } catch (Exception e) {
            publishLogProcess.warn(e.getMessage());
            msg = "git已经初始化！";
            publishLogProcess.info("git已经初始化！");
        }
        return msg;
    }

    /**
     * 获取git对象
     *
     * @return
     */
    public static Git getRootGit() {
        try {
            publishLogProcess.info("git地址："+LOCALGITFILE);
            return new Git(new FileRepository(LOCALGITFILE));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @描述 pull拉取远程仓库文件
     * @Param
     * @返回值 boolean
     * @创建人-时间 by - 2022/2/24
     */
    public static boolean pullBranchToLocal() {
        boolean resultFlag = false;
        //git仓库地址
        Git git;
        try {
            git = new Git(new FileRepository(LOCALGITFILE));
            git.pull()
                    .setCredentialsProvider(JGitHandle.user)
                    .setRemoteBranchName("master")
                    .call();
            resultFlag = true;
        } catch (GitAPIException | IOException e) {
            e.printStackTrace();
        }
        return resultFlag;
    }

    /**
     * 获取Git本地仓库版本号
     *
     * @return versionID 版本号
     * @throws IOException
     * @throws GitAPIException
     */
    public static String getLog() {
        String versionID = "";
        Repository localRepo;
        Git git = getRootGit();

        Iterable<RevCommit> iterable = null;
        try {
            iterable = git.log().all().call();
        } catch (GitAPIException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (RevCommit u : iterable) {
            //版本id
            String ID = String.valueOf(u.getId());
            versionID = getMatcher("commit\\s(\\w+)\\s?", ID);
            break;
        }

        git.close();
        return versionID;
    }


    /**
     * getMatcher 正则表达式方法
     *
     * @param regex
     * @param source
     * @return
     */
    public static String getMatcher(String regex, String source) {
        String str = "";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(source);
        while (matcher.find()) {
            str = matcher.group(1); // 只取第一组
        }
        return str;
    }


    //提交git
    public static boolean commitFiles() {
        Git git = null;
        try {
            git = Git.open(new File(LOCALGITFILE));
            AddCommand addCommand = git.add();
            //add操作 add -A操作在jgit不知道怎么用 没有尝试出来 有兴趣的可以看下jgitAPI研究一下 欢迎留言
            addCommand.addFilepattern(".").call();

            RmCommand rm = git.rm();
            Status status = git.status().call();
            //循环add missing 的文件 没研究出missing和remove的区别 就是删除的文件也要提交到git
            Set<String> missing = status.getMissing();
            for (String m : missing) {
                rm.addFilepattern(m).call();
                //每次需重新获取rm status 不然会报错
                rm = git.rm();
                status = git.status().call();
            }
            //循环add remove 的文件
            Set<String> removed = status.getRemoved();
            for (String r : removed) {
                rm.addFilepattern(r).call();
                rm = git.rm();
                status = git.status().call();
            }
            //提交
            git.commit().setMessage("commit").call();
            //推送
            git.push().setCredentialsProvider(new UsernamePasswordCredentialsProvider(USER, PASSWORD)).call();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取增量包，并将增量包拷贝到指定的文件夹
     *
     * @param lastId
     * @return
     */
    public static boolean diffToChangeFiles(String lastId) {
        Git git;
        try {
            git = new Git(new FileRepository(LOCALGITFILE));
            //获取最后一次拉取的

            String newId = git.getRepository().resolve("HEAD").getName();
//            String oldId = git.getRepository().resolve("HEAD~").getName();
            AbstractTreeIterator newTreeIter = prepareTreeParser(git.getRepository(), newId);
            AbstractTreeIterator oldTreeIter = prepareTreeParser(git.getRepository(), lastId);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();


//            git.archive().setFormat("zip").setPaths(LOCALPATH + File.separator + "update.zip").call();
            List<DiffEntry> updateList = git.diff()
                    .setNewTree(newTreeIter)  //设置源，不设置则默认工作区和历史最新commit版本比较
                    .setOldTree(oldTreeIter)

                    .setShowNameAndStatusOnly(true)  //设置是否只返回更改文件的名称和状态
//                    .setPathFilter(PathFilter.create(".txt"))  //设置过滤
                    .setOutputStream(outputStream) //输出流  用outputStream，后面转成字符串

                    .call();

            //更新下来的文件的路径进行记录--这里记录全路径---这里只能记录新增的文件，如果是删除的文件，则获取不到
            List<String> diffFilePathList = new ArrayList();
            for (DiffEntry diffEntry : updateList) {
                //获取更新列表
                String newPath = diffEntry.getNewPath();
                //如果当前列表，是新增的则记录当前文件
                if (diffEntry.getChangeType() != null
                        && ("ADD".equals(diffEntry.getChangeType().name()) || "MODIFY".equals(diffEntry.getChangeType().name()))) {
                    diffFilePathList.add(newPath);
                }
            }
            // 对文件的处理，将更新下的文件备份到指定的目录下
            File updateFile = new File(updateSavePath);
            //如果不存在，则创建
            if (!updateFile.exists()) {
                updateFile.mkdir();
            } else {
                //如果存在，则清空里面的文件
                UtilFile.deleteFile(updateFile);
            }
            publishLogProcess.info("拉取到的文件为：");
            publishLogProcess.info(diffFilePathList);
            boolean isCopyFlag = true;
            for (String path : diffFilePathList) {
                boolean flag = UtilFile.copyFile(LOCALPATH + File.separator + path, updateSavePath + File.separator + path);
                if (!flag) {
                    isCopyFlag = false;
                }
            }
//            List<DiffEntry> diff = git.diff().setOldTree(oldTree).setNewTree(newTree).setShowNameAndStatusOnly(true).call();


        } catch (IOException | GitAPIException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 获取本地--未使用
     */
    public static void getLocalChangeFile() {
        Git git;
        try {
            git = new Git(new FileRepository(LOCALGITFILE));

            DiffCommand diff = git.diff();
            List<DiffEntry> call = diff.call();
            List<String> diffFileList = new ArrayList();
            for (DiffEntry diffEntry : call) {
                String newPath = diffEntry.getNewPath();
                System.out.println(newPath);
                if (newPath.indexOf("/") > 0)
                    diffFileList.add(newPath.substring(newPath.lastIndexOf("/") + 1));
                else
                    diffFileList.add(newPath);
            }
        } catch (IOException | GitAPIException e) {
            e.printStackTrace();
        }
        System.out.println("变更文件列表如下:");
        //diffFileList.stream().forEach(System.out::println);
    }


    public static AbstractTreeIterator prepareTreeParser(Repository repository, String objectId) {
        try (RevWalk walk = new RevWalk(repository)) {
            RevCommit commit = null;
            try {
                commit = walk.parseCommit(ObjectId.fromString(objectId));
                RevTree tree = null;
                tree = walk.parseTree(commit.getTree().getId());

                CanonicalTreeParser treeParser = new CanonicalTreeParser();
                try (ObjectReader reader = repository.newObjectReader()) {
                    treeParser.reset(reader, tree.getId());
                }
                walk.dispose();
                return treeParser;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }


}
